#pragma once
#include <iostream>
#include <pthread.h>
#include <queue>
#include <unistd.h>
#include <string>
#include <cstdlib>
#include <time.h>
#define QUE_NUM 10

// template<class T>
// class BlockQueue
// {

// public:
//     //构造函数
//     BlockQueue(pthread_mutex_t* pmutex, pthread_cond_t* pcond_producers , pthread_cond_t* pcond_consumer)
//         :_pmutex(pmutex)
//         , _pcond_consumer(pcond_consumer)
//         , _pcond_producers(pcond_producers)
//     {
//         pthread_cond_init(_pcond_consumer, nullptr);
//         pthread_cond_init(_pcond_producers, nullptr);
//         pthread_mutex_init(_pmutex, nullptr);
//     }

//     //析构函数
//     ~BlockQueue()
//     {
//         pthread_cond_destroy(_pcond_producers);
//         pthread_cond_destroy(_pcond_consumer);
//         pthread_mutex_destroy(_pmutex);
//     }

//     void Push(const T& val)
//     {
//         //先保证互斥访问队列
//         pthread_mutex_lock(_pmutex);

//         while(_que.size() == QUE_NUM)
//         {
//             //阻塞当前生产者进程
//             pthread_cond_wait(_pcond_producers, _pmutex);
//         }
//         _que.push(val);
//         //唤醒一个消费者进程
//         pthread_cond_signal(_pcond_consumer);//和下面的释放互斥锁先后顺序没关系
//         //释放互斥锁
//         pthread_mutex_unlock(_pmutex);
//     }

//     void Pop(T*const pval)
//     {
//         //先保证互斥访问队列
//         pthread_mutex_lock(_pmutex);

//         //如果当前队列没有产品
//         while(_que.size() == 0 )//这里是循环检测 想想为什么？
//         {
//             //阻塞当前消费者进程
//             pthread_cond_wait(_pcond_consumer, _pmutex);
//         }
//         *pval = _que.front();
//         _que.pop();

//         // //当队列的产品低于7 个的时候开始生产
//         // if(_que.size() < 7)
//         // {
//         //     pthread_cond_signal(_pcond_producers);
//         // }
//         pthread_cond_signal(_pcond_producers);

//         pthread_mutex_unlock(_pmutex);
//     }

// private:
//     std::queue<T> _que;
//     //创建一个互斥锁
//     pthread_mutex_t* _pmutex;

//     //创建一个消费者条件变量
//     pthread_cond_t* _pcond_consumer;

//     //创建一个生产者条件变量
//     pthread_cond_t* _pcond_producers;
// };

template <class T>
class BlockQueue
{
public:
    BlockQueue(const int cap = 5) : _cap(cap)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_pcond, nullptr);
        pthread_cond_init(&_ccond, nullptr);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_pcond);
        pthread_cond_destroy(&_ccond);
    }

    void push(const T &in)
    {
        pthread_mutex_lock(&_mutex);
        while(_queue.size() == _cap)
        {
            // 生产者该阻塞了
            pthread_cond_wait(&_pcond, &_mutex);
            //该函数调用的时候会把_mutex互斥量释放并且，将自己线程挂起到_pcond条件变量中等待。
            //在此处挂起再唤醒的时候会在此处被唤醒，所有上面的条件判断应该改为while，以防万一。
            //此函数再被唤醒的时候也会自动的重新唤醒_mutex互斥锁。
        }
        // 走到这里说明还有空间。
        _queue.push(in);

        // 这里说明队列里面有数据了
        pthread_cond_signal(&_ccond); // 唤醒消费者
        // 这里可以有一定的策略，判断是否唤醒。
        //这个唤醒的操作可以放在临界区里面也可也放在临界区外面
        pthread_mutex_unlock(&_mutex);
    }

    void pop(T *const out) // 输出型参数
    {
        pthread_mutex_lock(&_mutex);
        while(_queue.size() == 0)
        {
            pthread_cond_wait(&_ccond, &_mutex);
        }

        *out = _queue.front();
        _queue.pop();

        pthread_cond_signal(&_pcond); // 唤醒生产者
        pthread_mutex_unlock(&_mutex); 
    }

private:
    std::queue<T> _queue;
    int _cap; // 队列的上限

    pthread_mutex_t _mutex; // 保护队列_queue

    pthread_cond_t _pcond; // 生产者对应的条件变量

    pthread_cond_t _ccond; // 消费者对应的条件变量
};